﻿#ifndef TIMERWHEEL_H
#define TIMERWHEEL_H

#include <vector>
#include <functional>
#include <memory>
#include <cstdint>
#include <unordered_map>


class TimerTask
{
private:
    using TaskFunc_t = std::function<void()>;
    using RealseTaskFunc_t = std::function<void()>;

    uint64_t m_id;             // 任务id
    uint32_t m_timeout;        // 定时长短
    bool m_isCanceled;         // 定时任务的状态
    TaskFunc_t m_taskCb;       // 执行任务的回调函数
    RealseTaskFunc_t m_realse; // 释放该任务在时间轮的信息的回调函数
public:
    TimerTask(uint64_t id, uint32_t timeout, const TaskFunc_t &cb);
    // NOTE: 析构时，执行任务，并释放在时间论的信息(id关联的指针)
    ~TimerTask();
    // 设置释放任务在时间轮的信息的回调函数
    void setRealse(const RealseTaskFunc_t &rcb);
    // 获取任务的定时长短
    uint32_t getTimeout();
    // 取消任务
    void cancel();
};


using TaskFunc_t = std::function<void()>;
class EventLoop;
class Channel;

class TimerWheel
{
private:
    using WtaskPtr = std::weak_ptr<TimerTask>;
    using StaskPtr = std::shared_ptr<TimerTask>;
    int m_tick;                                      // 时间针
    int m_maxcap;                                    // 时间轮最大容量: 秒轮-60
    std::vector<std::vector<StaskPtr>> m_wheel;     // 时间轮
    std::unordered_map<uint64_t, WtaskPtr> m_infos; // 时间轮存储的所有任务信息 -- id:weak_ptr
    EventLoop *m_eloop;
    int m_timerfd; // 时间通知机制，每隔(自定义:1s)时间，通知时间针移动
    std::unique_ptr<Channel> m_timerfd_channel;

private:
    // 创建时间通知机制
    static int CreateTimerfd();
    // 删除任务信息
    // NOTE: 任务的回调函数，在析构时，会自动调用，移除infos里对应id的任务信息
    void removeInfo(uint64_t id);

    // 添加任务
    void _addTimerTask(uint64_t id, uint32_t timeout, const TaskFunc_t &task);
    // 刷新任务
    void _refreshTimerTask(uint64_t id);
    // 取消任务
    void _cancelTimerTask(uint64_t id);

    // 读取时间通知机制
    int readTimerFd();
    // 时间轮转动
    void goTick();
public:
    TimerWheel(EventLoop *eloop);
    ~TimerWheel();
    // 添加任务
    void addTimerTask(uint64_t id, uint32_t timeout, const TaskFunc_t &task);
    // 刷新任务
    void refreshTimerTask(uint64_t id);
    // 取消任务
    void cancelTimerTask(uint64_t id);
    // 判断任务是否存在
    // NOTE: 这个接口访问m_infos, 且没有RunInLoop, 因此它是存在线程安全问题，但是这个函数只在服务器组件内部使用，因此在使用时注意一下
    bool hasTimerTask(uint64_t id);
    // 时间轮启动
    void start();
};





#endif
